﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using PacManLib;
using System.Diagnostics;
using System.Threading;

namespace PacManBoardControl
{
    /// <summary>
    /// A WinForms control to show the Pac-Man board.
    /// If it holds a reference to a PacManLib.Board object, then
    /// it renders the objects - including Pac-Man and the ghosts - 
    /// from that board.
    /// </summary>
    public partial class PacManBoard: UserControl
    {
        #region Public methods and properties

        /// <summary>
        /// Constructor
        /// </summary>
        public PacManBoard()
        {
            InitializeComponent();

            // We create the ghost bitmaps...
            for(int i=0; i<Board.NumberOfGhosts; ++i)
            {
                switch(i)
                {
                    case 0:
                        m_ghostBitmapManagers[i] = new GhostBitmapManager(RED_GHOST_COLOR);
                        break;
                    case 1:
                        m_ghostBitmapManagers[i] = new GhostBitmapManager(PINK_GHOST_COLOR);
                        break;
                    case 2:
                        m_ghostBitmapManagers[i] = new GhostBitmapManager(BLUE_GHOST_COLOR);
                        break;
                    case 3:
                        m_ghostBitmapManagers[i] = new GhostBitmapManager(YELLOW_GHOST_COLOR);
                        break;
                    default:
                        m_ghostBitmapManagers[i] = new GhostBitmapManager(GREEN_GHOST_COLOR);
                        break;
                }
            }
        }

        /// <summary>
        /// Shows the board.
        /// </summary>
        public void show()
        {
            m_pacManPosition = getPacManCoordinates();
            for (int i = 0; i < Board.NumberOfGhosts; ++i)
            {
                m_ghostPositions[i] = getGhostCoordinates(i);
            }
            Invalidate();
        }

        /// <summary>
        /// Shows the board and "glides" the Pac-Man and ghosts from their
        /// previous position to the their new ones over the time period
        /// passed in.
        /// </summary>
        public void showGliding(int intervalMS)
        {
            // If the interval is 0 (or negative) we just show 
            // the players directly at their destination points...
            if (intervalMS <= 0)
            {
                m_pacManPosition = getPacManCoordinates();
                for (int i = 0; i < Board.NumberOfGhosts; ++i)
                {
                    m_ghostPositions[i] = getGhostCoordinates(i);
                }
                Invalidate();
                Application.DoEvents();
                return;
            }

            // We create "animators" to help animate Pac-Man and the ghosts...
            PositionAnimator pacManAnimator = new PositionAnimator(m_pacManPosition, getPacManCoordinates());
            PositionAnimator[] ghostAnimators = new PositionAnimator[Board.NumberOfGhosts];
            for (int i = 0; i < Board.NumberOfGhosts; ++i)
            {
                ghostAnimators[i] = new PositionAnimator(m_ghostPositions[i], getGhostCoordinates(i));
            }

            // We animate the players between the start and end points over the time
            // interval passed in...
            m_stopwatch.Reset();
            m_stopwatch.Start();
            do
            {
                // We sleep, and then work out how far through the time period we are...
                Thread.Sleep(1);
                double timeFraction = m_stopwatch.ElapsedMilliseconds / (double)intervalMS;
                if (timeFraction > 1.0)
                {
                    timeFraction = 1.0;
                }

                // We find the new positions of Pac-Man and the ghosts...
                m_pacManPosition = pacManAnimator.getPosition(timeFraction);
                for (int i = 0; i < Board.NumberOfGhosts; ++i)
                {
                    m_ghostPositions[i] = ghostAnimators[i].getPosition(timeFraction);
                }

                // We show the board and the players in the new positions...
                Invalidate();
                Application.DoEvents();
            } while (m_stopwatch.ElapsedMilliseconds < intervalMS);
            m_stopwatch.Stop();
        }

        /// <summary>
        /// Gets or sets the board to display.
        /// </summary>
        public Board Board
        {
            get { return m_board; }
            set { m_board = value; }
        }

        /// <summary>
        /// Creates a Sound object.
        /// </summary>
        public Sound createSound(Sound.Sample sample)
        {
            return new Sound(sample);
        }

        #endregion

        #region Private functions

        /// <summary>
        /// Handler for the control's Paint event.
        /// </summary>
        private void PacManBoard_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            paintGameBoard(e.Graphics);
            paintPellets(e.Graphics);
            paintPacMan(e.Graphics);
            paintGhosts(e.Graphics);
        }

        /// <summary>
        /// Paints the Pac-Man.
        /// </summary>
        private void paintPacMan(Graphics g)
        {
            // We can only show the Pac-Man if we've got the board...
            if (m_board == null)
            {
                return;
            }
            PlayerInfo pacManInfo = m_board.PacMan;
            Bitmap bitmap = m_pacManBitmapManager.getBitmap(pacManInfo);
            g.DrawImageUnscaled(bitmap, m_pacManPosition.X, m_pacManPosition.Y);
        }

        /// <summary>
        /// Returns the Pac-Man position as coordinates where the graphic
        /// should be drawn.
        /// </summary>
        private Point getPacManCoordinates()
        {
            Point position = new Point(0, 0);
            if (m_board != null)
            {
                int boardX, boardY;
                convertCoordinates(m_board.PacMan.Position, out boardX, out boardY);
                boardX -= 5;
                boardY -= 3;
                position.X = boardX;
                position.Y = boardY;
            }
            return position;
        }

        /// <summary>
        /// Returns the ghost position as coordinates where the graphic
        /// should be drawn.
        /// </summary>
        private Point getGhostCoordinates(int index)
        {
            Point position = new Point(0, 0);
            if (m_board != null)
            {
                PlayerInfo ghostInfo = m_board.Ghosts[index];
                int boardX, boardY;
                convertCoordinates(ghostInfo.Position, out boardX, out boardY);
                boardX -= 6;
                boardY -= 3;
                position.X = boardX;
                position.Y = boardY;
            }
            return position;
        }

        /// <summary>
        /// Paints the ghosts.
        /// </summary>
        private void paintGhosts(Graphics g)
        {
            // We can only show the ghosts if we've got the board...
            if (m_board == null)
            {
                return;
            }

            // We show each ghost...
            for(int i=0; i<Board.NumberOfGhosts; ++i)
            {
                // We get the bitmap for the ghost...
                PlayerInfo ghostInfo = m_board.Ghosts[i];
                Bitmap bitmap = m_ghostBitmapManagers[i].getBitmap(ghostInfo);

                // And we show it...
                g.DrawImageUnscaled(bitmap, m_ghostPositions[i].X, m_ghostPositions[i].Y);
            }
        }

        /// <summary>
        /// Draws the maze.
        /// </summary>
        private void paintGameBoard(Graphics g)
        {
            SolidBrush b = new SolidBrush(Color.FromArgb(0, 51, 255));

            Pen p = new Pen(b, 2);

            // Outer Box
            g.DrawArc(p, 1, 1, 25, 25, 180, 90);
            g.DrawLine(p, 13, 1, 434, 1);
            g.DrawArc(p, 421, 1, 25, 25, 270, 90);
            g.DrawLine(p, 446, 13, 446, 164);
            g.DrawArc(p, 421, 151, 25, 25, 0, 90);
            g.DrawLine(p, 434, 176, 370, 176);
            g.DrawArc(p, 366, 176, 10, 10, 180, 90);
            g.DrawLine(p, 366, 181, 366, 227);
            g.DrawArc(p, 366, 221, 10, 10, 90, 90);
            g.DrawLine(p, 370, 231, 448, 231);
            g.DrawLine(p, 370, 282, 448, 282);
            g.DrawArc(p, 366, 282, 10, 10, 180, 90);
            g.DrawLine(p, 366, 284, 366, 333);
            g.DrawArc(p, 366, 327, 10, 10, 90, 90);
            g.DrawLine(p, 434, 337, 370, 337);
            g.DrawArc(p, 421, 337, 25, 25, 270, 90);
            g.DrawLine(p, 446, 347, 446, 535);
            g.DrawArc(p, 421, 522, 25, 25, 0, 90);
            g.DrawLine(p, 13, 547, 434, 547);
            g.DrawArc(p, 1, 522, 25, 25, 90, 90);
            g.DrawLine(p, 1, 535, 1, 347);
            g.DrawArc(p, 1, 337, 25, 25, 180, 90);
            g.DrawLine(p, 13, 337, 77, 337);
            g.DrawArc(p, 71, 327, 10, 10, 0, 90);
            g.DrawLine(p, 81, 333, 81, 287);
            g.DrawArc(p, 71, 282, 10, 10, 270, 90);
            g.DrawLine(p, 0, 282, 77, 282);
            g.DrawLine(p, 0, 231, 77, 231);
            g.DrawArc(p, 71, 221, 10, 10, 0, 90);
            g.DrawLine(p, 81, 181, 81, 227);
            g.DrawArc(p, 71, 176, 10, 10, 270, 90);
            g.DrawLine(p, 13, 176, 77, 176);
            g.DrawArc(p, 1, 151, 25, 25, 90, 90);
            g.DrawLine(p, 1, 10, 1, 166);

            // Inner Box
            g.DrawArc(p, 7, 8, 10, 10, 180, 90);
            g.DrawLine(p, 11, 8, 212, 8);
            g.DrawArc(p, 206, 8, 10, 10, 270, 90);
            g.DrawLine(p, 216, 12, 216, 75);
            g.DrawArc(p, 216, 69, 14, 10, 0, 180);
            g.DrawLine(p, 230, 12, 230, 75);
            g.DrawArc(p, 230, 8, 10, 10, 180, 90);
            g.DrawLine(p, 234, 8, 436, 8);
            g.DrawArc(p, 430, 8, 10, 10, 270, 90);
            g.DrawLine(p, 440, 12, 440, 165);
            g.DrawArc(p, 430, 159, 10, 10, 0, 90);
            g.DrawLine(p, 368, 169, 436, 169);
            g.DrawArc(p, 360, 169, 18, 18, 180, 90);
            g.DrawLine(p, 360, 178, 360, 230);
            g.DrawArc(p, 360, 220, 18, 18, 90, 90);
            g.DrawLine(p, 368, 238, 448, 238);
            g.DrawLine(p, 368, 275, 448, 275);
            g.DrawArc(p, 360, 275, 18, 18, 180, 90);
            g.DrawLine(p, 360, 284, 360, 336);
            g.DrawArc(p, 360, 326, 18, 18, 90, 90);
            g.DrawLine(p, 368, 344, 436, 344);
            g.DrawArc(p, 430, 344, 10, 10, 270, 90);
            g.DrawLine(p, 440, 349, 440, 430);
            g.DrawArc(p, 430, 424, 10, 10, 0, 90);
            g.DrawLine(p, 412, 434, 436, 434);
            g.DrawArc(p, 407, 434, 11, 16, 90, 180);
            g.DrawLine(p, 412, 450, 436, 450);
            g.DrawArc(p, 430, 450, 10, 10, 270, 90);
            g.DrawLine(p, 440, 455, 440, 536);
            g.DrawArc(p, 430, 530, 10, 10, 0, 90);
            g.DrawLine(p, 11, 540, 436, 540);
            g.DrawArc(p, 7, 530, 10, 10, 90, 90);
            g.DrawLine(p, 7, 455, 7, 536);
            g.DrawArc(p, 7, 450, 10, 10, 180, 90);
            g.DrawLine(p, 11, 450, 34, 450);
            g.DrawArc(p, 28, 434, 11, 16, 270, 180);
            g.DrawLine(p, 11, 434, 34, 434);
            g.DrawArc(p, 7, 424, 10, 10, 90, 90);
            g.DrawLine(p, 7, 349, 7, 430);
            g.DrawArc(p, 7, 344, 10, 10, 180, 90);
            g.DrawLine(p, 11, 344, 80, 344);
            g.DrawArc(p, 69, 326, 18, 18, 0, 90);
            g.DrawLine(p, 87, 284, 87, 336);
            g.DrawArc(p, 69, 275, 18, 18, 270, 90);
            g.DrawLine(p, 0, 275, 79, 275);
            g.DrawLine(p, 0, 238, 79, 238);
            g.DrawArc(p, 69, 220, 18, 18, 0, 90);
            g.DrawLine(p, 87, 178, 87, 230);
            g.DrawArc(p, 69, 169, 18, 18, 270, 90);
            g.DrawLine(p, 11, 169, 79, 169);
            g.DrawArc(p, 7, 159, 10, 10, 90, 90);
            g.DrawLine(p, 7, 12, 7, 165);

            // Bumpers
            g.DrawLine(p, 45, 45, 83, 45);
            g.DrawArc(p, 77, 45, 10, 10, 270, 90);
            g.DrawLine(p, 87, 50, 87, 75);
            g.DrawArc(p, 77, 69, 10, 10, 0, 90);
            g.DrawLine(p, 45, 79, 83, 79);
            g.DrawArc(p, 41, 69, 10, 10, 90, 90);
            g.DrawLine(p, 41, 50, 41, 75);
            g.DrawArc(p, 41, 45, 10, 10, 180, 90);
            //
            g.DrawLine(p, 125, 45, 179, 45);
            g.DrawArc(p, 173, 45, 10, 10, 270, 90);
            g.DrawLine(p, 183, 50, 183, 75);
            g.DrawArc(p, 173, 69, 10, 10, 0, 90);
            g.DrawLine(p, 125, 79, 179, 79);
            g.DrawArc(p, 121, 69, 10, 10, 90, 90);
            g.DrawLine(p, 121, 50, 121, 75);
            g.DrawArc(p, 121, 45, 10, 10, 180, 90);
            //
            g.DrawLine(p, 269, 45, 323, 45);
            g.DrawArc(p, 316, 45, 10, 10, 270, 90);
            g.DrawLine(p, 326, 50, 326, 75);
            g.DrawArc(p, 316, 69, 10, 10, 0, 90);
            g.DrawLine(p, 269, 79, 323, 79);
            g.DrawArc(p, 265, 69, 10, 10, 90, 90);
            g.DrawLine(p, 265, 50, 265, 75);
            g.DrawArc(p, 265, 45, 10, 10, 180, 90);
            //
            g.DrawLine(p, 364, 45, 403, 45);
            g.DrawArc(p, 396, 45, 10, 10, 270, 90);
            g.DrawLine(p, 406, 50, 406, 75);
            g.DrawArc(p, 396, 69, 10, 10, 0, 90);
            g.DrawLine(p, 364, 79, 404, 79);
            g.DrawArc(p, 360, 69, 10, 10, 90, 90);
            g.DrawLine(p, 360, 50, 360, 75);
            g.DrawArc(p, 360, 45, 10, 10, 180, 90);
            //
            g.DrawLine(p, 46, 116, 82, 116);
            g.DrawArc(p, 76, 116, 11, 16, 270, 180);
            g.DrawLine(p, 46, 132, 82, 132);
            g.DrawArc(p, 41, 116, 11, 16, 90, 180);

            // Top Center "T"
            g.DrawArc(p, 169, 116, 11, 16, 90, 180);
            g.DrawLine(p, 174, 116, 274, 116);
            g.DrawArc(p, 267, 116, 11, 16, 270, 180);
            g.DrawLine(p, 174, 132, 214, 132);
            g.DrawLine(p, 234, 132, 274, 132);
            g.DrawArc(p, 230, 132, 10, 10, 180, 90);
            g.DrawArc(p, 207, 132, 10, 10, 270, 90);
            g.DrawLine(p, 217, 137, 217, 180);
            g.DrawArc(p, 217, 174, 13, 11, 0, 180);
            g.DrawLine(p, 230, 137, 230, 180);
            //
            g.DrawLine(p, 365, 116, 401, 116);
            g.DrawArc(p, 395, 116, 11, 16, 270, 180);
            g.DrawLine(p, 365, 132, 401, 132);
            g.DrawArc(p, 360, 116, 11, 16, 90, 180);
            //
            g.DrawArc(p, 121, 116, 14, 11, 180, 180);
            g.DrawLine(p, 121, 120, 121, 233);
            g.DrawLine(p, 135, 120, 135, 165);
            g.DrawArc(p, 135, 159, 10, 10, 90, 90);
            g.DrawLine(p, 139, 169, 178, 169);
            g.DrawArc(p, 172, 169, 11, 16, 270, 180);
            g.DrawLine(p, 139, 185, 178, 185);
            g.DrawArc(p, 135, 185, 10, 10, 180, 90);
            g.DrawLine(p, 135, 190, 135, 233);
            g.DrawArc(p, 121, 227, 14, 11, 0, 180);
            //
            g.DrawArc(p, 312, 116, 14, 11, 180, 180);
            g.DrawLine(p, 326, 120, 326, 233);
            g.DrawLine(p, 312, 120, 312, 165);
            g.DrawArc(p, 302, 159, 10, 10, 0, 90);
            g.DrawLine(p, 308, 169, 268, 169);
            g.DrawArc(p, 264, 169, 11, 16, 90, 180);
            g.DrawLine(p, 308, 185, 268, 185);
            g.DrawArc(p, 302, 185, 10, 10, 270, 90);
            g.DrawLine(p, 312, 190, 312, 233);
            g.DrawArc(p, 312, 227, 14, 11, 0, 180);

            //Box
            SolidBrush b2 = new SolidBrush(Color.FromArgb(255, 189, 228));
            g.FillRectangle(b2, 207, 223, 33, 5);
            g.DrawLine(p, 168, 222, 207, 222);
            g.DrawLine(p, 169, 222, 169, 291);
            g.DrawLine(p, 168, 291, 279, 291);
            g.DrawLine(p, 278, 291, 278, 222);
            g.DrawLine(p, 240, 222, 279, 222);
            g.DrawLine(p, 241, 222, 241, 229);
            g.DrawLine(p, 240, 229, 272, 229);
            g.DrawLine(p, 272, 284, 272, 228);
            g.DrawLine(p, 175, 284, 273, 284);
            g.DrawLine(p, 175, 229, 175, 285);
            g.DrawLine(p, 174, 229, 207, 229);
            g.DrawLine(p, 206, 222, 206, 229);

            g.DrawArc(p, 121, 275, 14, 11, 180, 180);
            g.DrawLine(p, 121, 280, 121, 339);
            g.DrawArc(p, 121, 333, 14, 11, 0, 180);
            g.DrawLine(p, 135, 280, 135, 339);

            g.DrawArc(p, 312, 275, 14, 11, 180, 180);
            g.DrawLine(p, 312, 280, 312, 339);
            g.DrawArc(p, 312, 333, 14, 11, 0, 180);
            g.DrawLine(p, 326, 280, 326, 339);

            g.DrawArc(p, 41, 381, 11, 16, 90, 180);
            g.DrawLine(p, 46, 381, 83, 381);
            g.DrawArc(p, 76, 381, 11, 16, 270, 90);
            g.DrawLine(p, 46, 397, 68, 397);
            g.DrawArc(p, 62, 397, 11, 16, 270, 90);
            g.DrawLine(p, 73, 405, 73, 445);
            g.DrawLine(p, 87, 389, 87, 445);
            g.DrawArc(p, 73, 439, 14, 11, 0, 180);

            g.DrawLine(p, 126, 381, 178, 381);
            g.DrawArc(p, 171, 381, 11, 16, 270, 180);
            g.DrawLine(p, 126, 397, 178, 397);
            g.DrawArc(p, 121, 381, 11, 16, 90, 180);

            // Mid Center "T"
            g.DrawArc(p, 169, 328, 11, 16, 90, 180);
            g.DrawLine(p, 174, 328, 274, 328);
            g.DrawArc(p, 267, 328, 11, 16, 270, 180);
            g.DrawLine(p, 174, 344, 214, 344);
            g.DrawLine(p, 234, 344, 274, 344);
            g.DrawArc(p, 230, 344, 10, 10, 180, 90);
            g.DrawArc(p, 207, 344, 10, 10, 270, 90);
            g.DrawLine(p, 217, 349, 217, 392);
            g.DrawArc(p, 217, 386, 13, 11, 0, 180);
            g.DrawLine(p, 230, 349, 230, 392);

            g.DrawLine(p, 270, 381, 322, 381);
            g.DrawArc(p, 315, 381, 11, 16, 270, 180);
            g.DrawLine(p, 270, 397, 322, 397);
            g.DrawArc(p, 265, 381, 11, 16, 90, 180);

            g.DrawArc(p, 360, 381, 11, 16, 180, 90);
            g.DrawLine(p, 363, 381, 402, 381);
            g.DrawArc(p, 395, 381, 11, 16, 270, 180);
            g.DrawLine(p, 378, 397, 402, 397);
            g.DrawArc(p, 374, 397, 11, 16, 180, 90);
            g.DrawLine(p, 374, 405, 374, 445);
            g.DrawLine(p, 360, 389, 360, 445);
            g.DrawArc(p, 360, 439, 14, 11, 0, 180);

            g.DrawArc(p, 41, 487, 11, 16, 90, 180);
            g.DrawLine(p, 45, 487, 117, 487);
            g.DrawArc(p, 111, 477, 10, 10, 0, 90);
            g.DrawLine(p, 121, 438, 121, 483);
            g.DrawArc(p, 121, 434, 14, 11, 180, 180);
            g.DrawLine(p, 135, 438, 135, 483);
            g.DrawArc(p, 135, 477, 10, 10, 90, 90);
            g.DrawLine(p, 138, 487, 179, 487);
            g.DrawArc(p, 171, 487, 11, 16, 270, 180);
            g.DrawLine(p, 45, 503, 179, 503);

            // Bottom Center "T"
            g.DrawArc(p, 169, 434, 11, 16, 90, 180);
            g.DrawLine(p, 174, 434, 274, 434);
            g.DrawArc(p, 267, 434, 11, 16, 270, 180);
            g.DrawLine(p, 174, 450, 214, 450);
            g.DrawLine(p, 234, 450, 274, 450);
            g.DrawArc(p, 230, 450, 10, 10, 180, 90);
            g.DrawArc(p, 207, 450, 10, 10, 270, 90);
            g.DrawLine(p, 217, 455, 217, 498);
            g.DrawArc(p, 217, 492, 13, 11, 0, 180);
            g.DrawLine(p, 230, 455, 230, 498);

            g.DrawArc(p, 265, 487, 11, 16, 90, 180);
            g.DrawLine(p, 270, 487, 308, 487);
            g.DrawArc(p, 302, 477, 10, 10, 0, 90);
            g.DrawLine(p, 312, 438, 312, 483);
            g.DrawArc(p, 312, 434, 14, 11, 180, 180);
            g.DrawLine(p, 326, 438, 326, 483);
            g.DrawArc(p, 326, 477, 10, 10, 90, 90);
            g.DrawLine(p, 330, 487, 402, 487);
            g.DrawArc(p, 395, 487, 11, 16, 270, 180);
            g.DrawLine(p, 270, 503, 402, 503);
        }

        /// <summary>
        /// Draws the pellets.
        /// </summary>
        private void paintPellets(Graphics g)
        {
            // We can only draw pellets if we have a board...
            if (m_board == null)
            {
                return;
            }

            // We loop through the items on the board, finding
            // and pellets...
            for (int x = 0; x < m_board.Width; ++x)
            {
                for (int y = 0; y < m_board.Height; ++y)
                {
                    switch (m_board.Items[x, y])
                    {
                        case Board.Item.Pellet:
                            paintPellet(g, x, y);
                            break;

                        case Board.Item.PowerPellet:
                            paintPowerPellet(g, x, y);
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Draws a pellet in the tile passed in.
        /// </summary>
        private void paintPellet(Graphics g, int x, int y)
        {
            int boardX, boardY;
            convertCoordinates(x, y, out boardX, out boardY);
            boardX += 5;
            boardY += 6;
            g.FillEllipse(m_pelletBrush, boardX, boardY, 5, 5);
        }

        /// <summary>
        /// Draws a power-pellet in the tile passed in.
        /// </summary>
        private void paintPowerPellet(Graphics g, int x, int y)
        {
            int boardX, boardY;
            convertCoordinates(x, y, out boardX, out boardY);
            boardY += 1;
            g.FillEllipse(m_powerPelletBrush, boardX, boardY, 15, 15);
        }

        /// <summary>
        /// Converts game-board tile coordinates into drawing coordinates.
        /// </summary>
        private void convertCoordinates(int x, int y, out int boardX, out int boardY)
        {
            boardX = (int)(x * TILE_WIDTH);
            boardY = (int)(y * TILE_HEIGHT);
        }

        /// <summary>
        /// Converts game-board tile coordinates into drawing coordinates.
        /// </summary>
        private void convertCoordinates(Point tile, out int boardX, out int boardY)
        {
            convertCoordinates(tile.X, tile.Y, out boardX, out boardY);
        }

        /// <summary>
        /// Called when the timer ticks.
        /// </summary>
        private void timer1_Tick(object sender, EventArgs e)
        {
            // We change the position of Pac-Man's mouth...
            m_pacManBitmapManager.nextPacManMouthPosition();

            // We change the position of the ghosts' legs...
            foreach (GhostBitmapManager ghostBitmapManager in m_ghostBitmapManagers)
            {
                ghostBitmapManager.nextLegPosition();
            }

            // We blink the power-pellets by changing the alpha of
            // their drawing color...
            int alpha = m_powerPelletBrush.Color.A;
            if (m_powerPelletAlphaIncreasing)
            {
                alpha += 50;
                if (alpha > 255)
                {
                    alpha = 255;
                    m_powerPelletAlphaIncreasing = false;
                }
            }
            else
            {
                alpha -= 50;
                if (alpha < 0)
                {
                    alpha = 0;
                    m_powerPelletAlphaIncreasing = true;
                }
            }
            m_powerPelletBrush.Color = Color.FromArgb(alpha, 255, 150, 150);

            Invalidate();
        }

        #endregion

        #region Constants

        // The size of a board tile...
        private const double TILE_WIDTH = 16.0;
        private const double TILE_HEIGHT = 17.666;

        // Ghost colors...
        private Color RED_GHOST_COLOR = Color.FromArgb(255, 0, 0);
        private Color PINK_GHOST_COLOR = Color.FromArgb(255, 184, 222);
        private Color BLUE_GHOST_COLOR = Color.FromArgb(0, 255, 222);
        private Color YELLOW_GHOST_COLOR = Color.FromArgb(255, 184, 71);
        private Color GREEN_GHOST_COLOR = Color.FromArgb(128, 255, 128);

        #endregion

        #region Private data

        // The board that we are showing...
        private Board m_board = null;

        // Brush for pellets...
        private SolidBrush m_pelletBrush = new SolidBrush(Color.FromArgb(255, 194, 159));
        private SolidBrush m_powerPelletBrush = new SolidBrush(Color.FromArgb(255, 150, 150));
        private bool m_powerPelletAlphaIncreasing = true;

        // Manages the Pac-Man bitmaps...
        private PacManBitmapManager m_pacManBitmapManager = new PacManBitmapManager();

        // Managers for the ghost bitmaps...
        private GhostBitmapManager[] m_ghostBitmapManagers = new GhostBitmapManager[Board.NumberOfGhosts];

        // The point where Pac-Man is drawn...
        private Point m_pacManPosition = new Point(0, 0);

        // The points where the ghosts are drawn...
        private Point[] m_ghostPositions = new Point[4];

        // Used for timing animations...
        private Stopwatch m_stopwatch = new Stopwatch();

        #endregion

    }

}
